Skip to content

docs: add adr for targeting metadata - #2013

Open
suthar26 wants to merge 1 commit into
open-feature:mainfrom
suthar26:docs-adr-targeting-metadata
Open

docs: add adr for targeting metadata#2013
suthar26 wants to merge 1 commit into
open-feature:mainfrom
suthar26:docs-adr-targeting-metadata

Conversation

@suthar26

@suthar26 suthar26 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

This PR

  • adds a new ADR for targeting metadata in flagd schema

Related Issues

Notes

Follow-up Tasks

How to test

@suthar26
suthar26 requested review from a team as code owners August 5, 2026 18:25
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Aug 5, 2026
@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for polite-licorice-3db33c ready!

Name Link
🔨 Latest commit 490029e
🔍 Latest deploy log https://app.netlify.com/projects/polite-licorice-3db33c/deploys/6a7c7e475d7b5800088bfd3d
😎 Deploy Preview https://deploy-preview-2013--polite-licorice-3db33c.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@suthar26
suthar26 force-pushed the docs-adr-targeting-metadata branch from f6c88cc to 986a12c Compare August 5, 2026 18:25
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an architecture decision for targeting results that support existing variant strings or objects with a variant and optional metadata. It documents validation, decoding, merge precedence, parse errors, telemetry, compatibility, implementation impacts, and open questions.

Changes

Targeting variant metadata

Layer / File(s) Summary
Targeting metadata architecture decision
docs/architecture-decisions/targeting-variant-metadata.md
Documents the tagged-object result shape, schema constraints, typed evaluation behavior, metadata precedence, parse-error handling, chained and nested targeting examples, telemetry, compatibility expectations, implementation coverage, and open design questions.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: aepfli, lukas-reining

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the documentation change and the targeting metadata topic.
Description check ✅ Passed The description directly states that the pull request adds an ADR for targeting metadata in the flagd schema.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@suthar26
suthar26 force-pushed the docs-adr-targeting-metadata branch 2 times, most recently from df15c70 to 79f3a25 Compare August 5, 2026 18:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/architecture-decisions/targeting-variant-metadata.md`:
- Around line 23-24: Revise the metadata plumbing statement to distinguish the
currently supported paths: confirm metadata for OFREP single/bulk and single
gRPC responses, note that this proto layer’s ResolveAll path does not yet expose
it, and remove the claim that RecordEvaluation telemetry already carries
metadata attributes. Limit the ADR’s end-to-end contract to these verified paths
and identify metrics/telemetry support as a separate change.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e45a8980-8e18-4872-8ea0-f2e078b09b03

📥 Commits

Reviewing files that changed from the base of the PR and between bbb05d4 and 986a12c.

📒 Files selected for processing (1)
  • docs/architecture-decisions/targeting-variant-metadata.md

Comment on lines +23 to +24
The plumbing to carry metadata already exists end to end: `AnyValue.Metadata` (`core/pkg/evaluator/ievaluator.go`) is `map[string]interface{}`, threaded through every resolver path and out through gRPC and OFREP as evaluation metadata that OpenFeature SDKs surface as `flagMetadata`.
The only thing missing is a way for a *rule branch* to contribute to it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 \
  'value\.Metadata|Metadata:|RecordEvaluation|AnyFlag|flagMetadata|OFREP' \
  --glob '*.go' --glob '*.proto' --glob '*.json' . || true

Repository: open-feature/flagd

Length of output: 50375


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== candidate files =="
git ls-files | rg 'flag_evaluator.*\.go$|ofrep|ievaluator|metrics|resol' | sed -n '1,200p'

echo
echo "== relevant source slices =="
sed -n '60,145p' flagd/pkg/service/flag-evaluation/flag_evaluator.go
sed -n '130,145p' flagd/pkg/service/flag-evaluation/flag_evaluator_v1.go
sed -n '360,405p' flagd/pkg/service/flag-evaluation/flag_evaluator.go
sed -n '245,270p' flagd/pkg/service/flag-evaluation/flag_evaluator_v2.go

echo
echo "== OFREP response metadata occurrences =="
rg -n -C 5 'metadata|Metadata|Resolve.*Response|flagMetadata|flag_metadata|recordEvaluation|Evaluation\(' flagd/pkg/service/ofrep **/ofrep*.go --glob '!*vendor*' --glob '!*testdata*' --glob '!*test' || true

Repository: open-feature/flagd

Length of output: 7340


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== OFREP handler/source slices =="
sed -n '1,260p' flagd/pkg/service/flag-evaluation/ofrep/handler.go
sed -n '1,220p' flagd/pkg/service/flag-evaluation/ofrep/ofrep_service.go

echo
echo "== schema V1 Proto AnyFlag messages =="
rg -n -C 4 'message AnyFlag|field Metadata|Metadata' proto schema flagd --glob '*.proto' --glob '*.pb.go' --glob '*.go' || true

echo
echo "== response result interface implementations =="
rg -n -C 4 'SetResult|RecordEvaluation|func .*SetResult|RecordEvaluation\(' flagd core flagd-proxy --glob '*.go' --glob '!*vendor*' || true

Repository: open-feature/flagd

Length of output: 50375


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== OFREP response model slices =="
sed -n '1,260p' core/pkg/service/ofrep/models.go

echo
echo "== OFREP test cases around metadata =="
sed -n '1,320p' flagd/pkg/service/flag-evaluation/ofrep/handler_test.go
sed -n '1,160p' core/pkg/service/ofrep/models_test.go

echo
echo "== OFREP service serializer occurrences =="
rg -n -C 4 'SuccessResponseFrom|BulkEvaluationResponseFrom|AnyValue|Metadata|flagMetadata|flag_metadata' core/pkg/service/ofrep --glob '*.go' || true

Repository: open-feature/flagd

Length of output: 33279


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== RecordEvaluation definitions =="
sed -n '1,220p' core/pkg/telemetry/metrics.go
rg -n -C 6 'func .*RecordEvaluation|RecordEvaluation.*Metadata|metadata|Metadata' core pkg flagd --glob '*.go' --glob '!*test.go' | sed -n '1,240p'

echo
echo "== AnyFlag schema declarations in vendored/generated/proto deps =="
rg -n -C 4 'message AnyFlag|AnyFlag|Metadata.*Struct|struct FieldMetadata' .vendor $GOPATH vendor flagd --glob '*.go' --glob '*.proto' --glob '*.pb.go' 2>/dev/null | sed -n '1,240p' || true

Repository: open-feature/flagd

Length of output: 26109


Clarify the metadata contract before calling the path end to end.

OFREP single and bulk responses carry AnyValue.Metadata, and single gRPC responses carry Metadata; ResolveAll now carries it through the v1 schema but not ResolveAll in this proto layer. RecordEvaluation(...) also does not expose metadata attributes yet, so the metrics/telemetry part of the ADR needs a separate change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture-decisions/targeting-variant-metadata.md` around lines 23 -
24, Revise the metadata plumbing statement to distinguish the currently
supported paths: confirm metadata for OFREP single/bulk and single gRPC
responses, note that this proto layer’s ResolveAll path does not yet expose it,
and remove the claim that RecordEvaluation telemetry already carries metadata
attributes. Limit the ADR’s end-to-end contract to these verified paths and
identify metrics/telemetry support as a separate change.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@aepfli

aepfli commented Aug 6, 2026

Copy link
Copy Markdown
Member

I generally like the idea, but I am missing the migration path. How do we plan to move from old version to new version, without breaking migration patterns?

I feel like we need to point out how we plan to migrate without breaking installations for others. In-process providers might be used in deprecated services and we need to give people time to migrate from one to the other.

Is it maybe time to have a version contract in the request for the flag configuration?

Signed-off-by: Parth Suthar <parth.suthar@dynatrace.com>
@suthar26
suthar26 force-pushed the docs-adr-targeting-metadata branch from 79f3a25 to 490029e Compare August 12, 2026 14:08
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Aug 12, 2026
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/architecture-decisions/targeting-variant-metadata.md`:
- Around line 29-45: Update the ADR to document the migration contract for the
new tagged-object return shape, including upgrade order, behavior when an older
provider rejects it or returns PARSE_ERROR, and whether configuration requests
include a schema or feature version. Clarify that existing string configurations
remain compatible but the new format is not forward-compatible, and remove any
claim of full backward compatibility until mixed-version rollout is defined.
- Around line 47-51: Extend integration tests around evaluateVariant to cover
string results, tagged objects with and without metadata, nested if branches,
and invalid result shapes. Ensure single-key {"variant":...} objects are
supported despite jsonlogic/v3.9.1 treating them as operators by adjusting
evaluateVariant or the dependency, while preserving PARSE_ERROR for unsupported
shapes and validating metadata merging.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f2ca8f5-e081-4794-b186-cbf48a00637b

📥 Commits

Reviewing files that changed from the base of the PR and between 79f3a25 and 490029e.

📒 Files selected for processing (1)
  • docs/architecture-decisions/targeting-variant-metadata.md

Comment on lines +29 to +45
Extend `definitions.primitive` in `schemas/json/targeting.json` with a tagged-object return shape:

```json
{
"type": "object",
"required": ["variant"],
"additionalProperties": false,
"properties": {
"variant": { "type": "string" },
"metadata": {
"$ref": "https://flagd.dev/schema/v0/flags.json#/definitions/metadata"
}
}
}
```

Reusing the existing `metadata` definition keeps values restricted to `string | number | boolean`, matching flag and flag-set metadata.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Document the migration and configuration-version contract before accepting this ADR.

When an older provider receives the new object return shape, it may reject the configuration or return PARSE_ERROR. Existing string configurations remain compatible, but the new configuration format is not forward-compatible with older providers.

Document the upgrade order, unsupported-provider behavior, and whether configuration requests carry a schema or feature version. Do not describe this as fully backwards compatible until mixed-version installations have a defined rollout path.

Also applies to: 47-51, 138-140

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture-decisions/targeting-variant-metadata.md` around lines 29 -
45, Update the ADR to document the migration contract for the new tagged-object
return shape, including upgrade order, behavior when an older provider rejects
it or returns PARSE_ERROR, and whether configuration requests include a schema
or feature version. Clarify that existing string configurations remain
compatible but the new format is not forward-compatible, and remove any claim of
full backward compatibility until mixed-version rollout is defined.

Comment on lines +47 to +51
In `evaluateVariant` (`core/pkg/evaluator/json.go`), replace the current string-strip of the JsonLogic result with a typed decode:

- string → variant key, as today
- object with `variant` field → use that as the variant key; shallow-merge `metadata` into the metadata already being returned
- anything else → `PARSE_ERROR`, same as an unrecognized return today

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== JsonLogic module declaration =="
rg -n 'github.com/diegoholiveira/jsonlogic' . \
  --glob 'go.mod' \
  --glob 'go.sum' || true

echo "== evaluator implementation and tests =="
rg -n -C 8 'evaluateVariant|jsonlogic|PARSE_ERROR' core \
  --glob '*.go'

Repository: open-feature/flagd

Length of output: 24028


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== current evaluator conversion path =="
sed -n '326,420p' core/pkg/evaluator/json.go

echo "== evaluator test files and relevant test declarations =="
git ls-files 'core/pkg/evaluator/*_test.go'
rg -n -C 5 'evaluateVariant|targeting|ParseErrorCode|PARSE_ERROR|metadata' core/pkg/evaluator --glob '*_test.go' | head -n 260

echo "== module declaration =="
sed -n '1,24p' core/go.mod

echo "== available local jsonlogic source, if any =="
find "${GOMODCACHE:-/nonexistent}" -path '*diegoholiveira*jsonlogic*' -type f 2>/dev/null | head -n 40 || true

Repository: open-feature/flagd

Length of output: 22003


🌐 Web query:

github.com/diegoholiveira/jsonlogic v3.9.1 Apply operator-free object literal behavior

💡 Result:

The version v3.9.1 of the diegoholiveira/jsonlogic Go library, released on April 30, 2026, includes various internal fixes [1], but the specific phrase "operator-free object literal behavior" is not explicitly mentioned in the official release notes [1][2]. However, based on the implementation details within the library, JSON objects are processed according to the following logic: 1. Identification of Operators: The library identifies operators by evaluating the map keys [3][4]. If a map contains exactly one key that matches a registered operator, it executes that operation [3][5]. 2. Operator-Free Objects: In the context of this library, if a JSON object (a map) has more than one key, or if the key does not match any registered operators, it is treated as a primitive (or a raw object literal) and returned as-is rather than being interpreted as a JsonLogic command [3][4]. This behavior allows the library to distinguish between JSON structures intended as logic rules and standard JSON objects that should be passed through as data literals [3]. This mechanism serves as a way to "skip" evaluation for objects that do not conform to the expected { "operator": [...] } rule format [3][4]. { "value": "The v3.9.1 release of the Go implementation of JsonLogic (diegoholiveira/jsonlogic) focuses on small fixes and internal improvements, including those related to operator evaluation and short-circuit logic [1][2]. While the specific phrase 'operator-free object literal behavior' is not explicitly defined in the official documentation, the library handles such structures by checking the number of keys and valid operators in a map. If a map contains multiple keys or an unrecognized operator, it is treated as a literal and returned without further recursive processing [3][4]. This design choice is fundamental to how the library prevents standard data objects from being incorrectly parsed as logic commands [3].", "citations": [3][4][1][2], "confidence": "high"}

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

base='https://raw.githubusercontent.com/diegoholiveira/jsonlogic/v3.9.1'

echo "== upstream evaluator implementation =="
curl -fsSL "$base/jsonlogic.go" | nl -ba | sed -n '1,260p'

echo "== upstream validator and operation handling =="
for file in validator.go operation.go; do
  echo "--- $file ---"
  curl -fsSL "$base/$file" | nl -ba | sed -n '1,240p'
done

echo "== upstream tests mentioning object literals, if, or Apply =="
for file in jsonlogic_test.go evaluator_test.go validator_test.go; do
  body="$(curl -fsSL "$base/$file" 2>/dev/null || true)"
  if [ -n "$body" ]; then
    echo "--- $file ---"
    printf '%s\n' "$body" | rg -n -C 4 'object|literal|Apply|if|map|variant' | head -n 220 || true
  fi
done

Repository: open-feature/flagd

Length of output: 309


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

base='https://raw.githubusercontent.com/diegoholiveira/jsonlogic/v3.9.1'

echo "== upstream evaluator implementation =="
curl -fsSL "$base/jsonlogic.go" | sed -n '1,280p'

echo "== upstream validator and operation handling =="
for file in validator.go operation.go; do
  echo "--- $file ---"
  curl -fsSL "$base/$file" | sed -n '1,260p'
done

echo "== upstream repository file list =="
curl -fsSL 'https://api.github.com/repos/diegoholiveira/jsonlogic/git/trees/v3.9.1?recursive=1' |
  grep -o '"path": "[^"]*"' |
  grep -E 'test|jsonlogic.go|validator.go|operation.go' |
  head -n 100

Repository: open-feature/flagd

Length of output: 11512


Add compatibility tests for tagged objects.

jsonlogic/v3.9.1 treats only maps with more than one key as literals. A {"variant":"blue"} result is treated as operator variant, so jsonlogic.Apply returns an error and evaluateVariant returns PARSE_ERROR. Add integration coverage through evaluateVariant for string returns, tagged objects with and without metadata, nested if branches, and invalid shapes. Adjust the evaluator or dependency if single-key tagged objects are supported.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture-decisions/targeting-variant-metadata.md` around lines 47 -
51, Extend integration tests around evaluateVariant to cover string results,
tagged objects with and without metadata, nested if branches, and invalid result
shapes. Ensure single-key {"variant":...} objects are supported despite
jsonlogic/v3.9.1 treating them as operators by adjusting evaluateVariant or the
dependency, while preserving PARSE_ERROR for unsupported shapes and validating
metadata merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants